/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2005-2007  Ben Motmans  <ben.motmans@gmail.com>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections.Generic;

using Gtk;

using Galaxium.Core;
using Anculus.Core;

namespace Galaxium.Gui.GtkGui
{
	public class GtkTreeView<T> : TreeView, ITreeBuilderRenderer<T> where T : ITreeNodeRenderer
	{
		private List<ITreeNodeBuilder<T>> _nodeBuilders;
		private ITreeNodeBuilder<T> _fallbackNodeBuilder;
		
		private Dictionary<uint, TreeIter> _iterLookup;
		private Dictionary<TreeIter, uint> _nodeLookup;
		private Dictionary<object, List<uint>> _objectLookup;
		
		private Dictionary<TreeIter, TreeIter> _dummyNodes;
		
		private TreeStore _treeStore;
		private TreeModelFilter _filteredTreeStore;
		private TreeModelSort _sortedTreeStore;
		
		private T _renderer;
		
		private bool _usingFilter;
		private bool _sort;
		private SortOrder _sortOrder;
		private ITreeNodeFilter _filter;
		private ITreeNodeSorter _sorter;
		
		private uint _nodeIndex = 0;
		
		private Queue<object> _objectQueue;
		private object _dummyObject;
		private bool _addDummy;
		
		//TODO: DnD
		//TODO: ITreeNodeRendere as arg + check all NodeBuilders for the generic renderer type
		//FIXME: errors if there is no sorter applied
		public GtkTreeView ()
		{
			_objectQueue = new Queue<object> ();
			_dummyObject = new object ();
			
			_nodeBuilders = new List<ITreeNodeBuilder<T>> ();
			_iterLookup = new Dictionary<uint, TreeIter> ();
			_nodeLookup = new Dictionary<TreeIter, uint> ();
			_objectLookup = new Dictionary<object, List<uint>> ();
			
			_treeStore = new TreeStore (typeof (object));
			_treeStore.RowInserted += new RowInsertedHandler (RowInsertedAfter);
			_treeStore.RowInserted += new RowInsertedHandler (RowInsertedBefore);
			_dummyNodes = new Dictionary<TreeIter, TreeIter> ();
			
			Model = _treeStore;
			
			_renderer = (T)GetTreeNodeRenderer ();
			
			if (_renderer == null)
				throw new InvalidOperationException ("The TreeView doesn't have a renderer!");
			
			InitializeLayout ();
			
			this.Selection.Changed += new EventHandler (GtkTreeViewSelectionChanged);
			this.RowActivated += new RowActivatedHandler (GtkTreeViewRowActivated);
			
			this.DragBegin += new DragBeginHandler (GtkTreeDragBegin);
			this.DragDataReceived += new DragDataReceivedHandler (GtkTreeDragDataReceived);
			this.DragDrop += new DragDropHandler (GtkTreeDragDrop);
			this.DragEnd += new DragEndHandler (GtkTreeDragEnd);
			this.DragMotion += new DragMotionHandler (GtkTreeDragMotion);
			
			this.HeadersVisible = false;
			this.Reorderable = true;
			this.ShowAll ();
		}
		
		protected void RowInsertedAfter (object sender, RowInsertedArgs args)
		{
			//this method is used to insert dummy nodes for every node that requires one
			if (!_addDummy)
				return;
			
			_addDummy = false;
			
			_objectQueue.Enqueue (_dummyObject);
			TreeIter dummy = _treeStore.AppendNode (args.Iter);
			_dummyNodes.Add (args.Iter, dummy);
		}
		
		[GLib.ConnectBefore]
		protected void RowInsertedBefore (object sender, RowInsertedArgs args)
		{
			//this method is required to store the object in the TreeStore BEFORE the filter and sort functions are called
			object obj = _objectQueue.Dequeue ();
			
			if (obj == _dummyObject)
				return;
			
			AddRealNode (args.Iter, obj);
		}
		
		public ITreeNodeBuilder<T> FallbackNodeBuilder
		{
			get { return _fallbackNodeBuilder; }
			set { _fallbackNodeBuilder = value; }
		}
		
		public T TreeNodeRenderer
		{
			get { return _renderer; }
		}
		
		protected override bool OnButtonPressEvent (Gdk.EventButton e)
		{
			bool retVal = base.OnButtonPressEvent (e);
			
			if (e.Button == 3)
			{
				TreePath path = null;
				
				if (GetPathAtPos ((int)e.X, (int)e.Y, out path))
				{
					TreeIter iter;
					if (Model.GetIter (out iter, path))
					{
						TreeNodeContext context = null;
						ITreeNodeBuilder<T> nodeBuilder = null;
						
						if (GetTreeNodeBuilder (iter, out context, out nodeBuilder, true))
						{
							Menu menu = MenuUtility.CreateContextMenu (nodeBuilder.ContextMenuExtensionPoint, new DefaultExtensionContext (context));
							
							if (menu != null)
							{
								menu.Popup (null, null, null, e.Button, e.Time);
								menu.ShowAll ();
							}
						}
					}
				}
			}
			
			return retVal;
		}
		
		protected virtual void GtkTreeViewRowActivated (object sender, RowActivatedArgs args)
		{
			TreeIter iter;
			
			if (Model.GetIter (out iter, args.Path))
			{
				TreeNodeContext context = null;
				ITreeNodeBuilder<T> nodeBuilder = null;
				
				if (GetTreeNodeBuilder (iter, out context, out nodeBuilder, true))
					nodeBuilder.NodeActivated (context);
			}
		}
		
		protected override void OnRowExpanded (TreeIter iter, TreePath path)
		{
			TreeIter realIter = GetRealTreeIter (iter);
			TreeIter dummy = TreeIter.Zero;
			
			if (_dummyNodes.TryGetValue (realIter, out dummy))
			{
				TreeNodeContext context = null;
				ITreeNodeBuilder<T> nodeBuilder = null;
				
				if (GetTreeNodeBuilder (realIter, out context, out nodeBuilder, false))
					nodeBuilder.BuildDynamicChildNodes (context);
				
				_dummyNodes.Remove (realIter);
				_treeStore.Remove (ref dummy);
			}
			
			base.OnRowExpanded (iter, path);
		}
		
		protected virtual void GtkTreeViewSelectionChanged (object sender, EventArgs e)
		{
		}
		
		protected virtual void GtkTreeDragBegin (object o, DragBeginArgs arg)
		{
		}
		
		protected virtual void GtkTreeDragDataReceived (object o, DragDataReceivedArgs args)
		{
		}
		
		protected virtual void GtkTreeDragDrop (object o, DragDropArgs args)
		{
		}
		
		protected virtual void GtkTreeDragEnd (object o, DragEndArgs args)
		{
		}
		
		[GLib.ConnectBefore]
		protected virtual void GtkTreeDragMotion (object o, DragMotionArgs args)
		{
		}
		
		protected virtual ITreeNodeRenderer GetTreeNodeRenderer ()
		{
			return new DefaultTreeNodeRenderer (_treeStore);
		}
		
		protected virtual void InitializeLayout ()
		{
			TreeViewColumn col = new TreeViewColumn ();
			
			CellRendererText textRenderer = new CellRendererText ();
			CellRendererPixbuf pixbufRenderer = new CellRendererPixbuf ();
			
			col.PackStart (pixbufRenderer, false);
			col.PackStart (textRenderer, true);
			
			col.SetCellDataFunc (textRenderer, new CellLayoutDataFunc (TextDataFunc));
			col.SetCellDataFunc (pixbufRenderer, new CellLayoutDataFunc (PixbufDataFunc));
			
			this.AppendColumn(col);
			
			ApplyLayout (col, textRenderer, pixbufRenderer);
		}
		
		protected virtual void ApplyLayout (TreeViewColumn col, CellRendererText textRenderer, CellRendererPixbuf pixbufRenderer)
		{
			col.Expand = true;
		}
		
		protected void TextDataFunc (CellLayout layout, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			TreeNodeContext context = null;
			ITreeNodeBuilder<T> nodeBuilder = null;
			
			if (GetTreeNodeBuilder (iter, out context, out nodeBuilder, true))
			{
				ITreeNodeWidget widget = GetTreeNodeWidget (_treeStore, layout, cell, iter);
				nodeBuilder.RenderText (context, _renderer, widget);
			}
		}
		
		protected void PixbufDataFunc (CellLayout layout, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			TreeNodeContext context = null;
			ITreeNodeBuilder<T> nodeBuilder = null;
			
			if (GetTreeNodeBuilder (iter, out context, out nodeBuilder, true))
			{
				ITreeNodeWidget widget = GetTreeNodeWidget (_treeStore, layout, cell, iter);
				nodeBuilder.RenderIcon (context, _renderer, widget);
			}
		}
		
		protected virtual bool GetTreeNodeBuilder (TreeIter iter, out TreeNodeContext context, out ITreeNodeBuilder<T> nodeBuilder, bool convertIter)
		{
			uint node;
			
			context = GetTreeNodeContext (iter, convertIter);
			
			if (context != null)
			{
				nodeBuilder = GetNodeBuilder (context.NodeObject);
				
				if (nodeBuilder == null)
					Log.Error("Unable to find TreeNodeBuilder for type '{0}'.", context.NodeObject == null ? "null" : context.NodeObject.GetType ().FullName);
				
				return nodeBuilder != null;
			}
			
			context = null;
			nodeBuilder = null;
			
			return false;
		}
		
		public TreeNodeContext GetTreeNodeContext (uint node)
		{
			TreeIter iter;
			
			if (_iterLookup.TryGetValue (node, out iter)) //this returns the real iter, so no conversion is needed
				return new TreeNodeContext (this, node, _treeStore.GetValue (iter, 0));
			
			return null;
		}
		
		public virtual TreeIter GetRealTreeIter (TreeIter iter)
		{			
			if (_usingFilter)
				iter = _filteredTreeStore.ConvertIterToChildIter (iter);
			
			if (_sort)
				iter = _sortedTreeStore.ConvertIterToChildIter (iter);
			
			return iter;
		}
		
		public virtual TreeIter GetVirtualTreeIter (TreeIter iter)
		{
			if (_sort)
				iter = _sortedTreeStore.ConvertChildIterToIter (iter);
			
			if (_usingFilter)
				iter = _filteredTreeStore.ConvertChildIterToIter (iter);
			
			return iter;
		}
		
		internal virtual TreeNodeContext GetTreeNodeContext (TreeIter iter, bool convertIter)
		{
			uint node;
			
			if (convertIter)
				iter = GetRealTreeIter (iter);
			
			if (_nodeLookup.TryGetValue (iter, out node))
				return new TreeNodeContext (this, node, _treeStore.GetValue (iter, 0));
			
			return null;
		}
		
		protected virtual ITreeNodeWidget GetTreeNodeWidget (TreeModel model, CellLayout layout, CellRenderer cell, TreeIter iter)
		{
			return new DefaultTreeNodeWidget (cell);
		}
		
		public ITreeNodeSorter Sorter
		{
			get { return _sorter; }
			set {
				if (_sorter != value)
				{
					_sorter = value;
					
					if (value != null)
					{
						_sort = true;
						
						if (_sortedTreeStore == null)
							this.Model = CreateTreeModel ();
						else
							this.Model = _sortedTreeStore;
					}
					else
					{
						_sort = false;
						
						if (_sortedTreeStore != null)
						{
							_sortedTreeStore.Dispose ();
							_sortedTreeStore = null;
						}
						
						if (_usingFilter)
							this.Model = _filteredTreeStore;
						else
							this.Model = _treeStore;
					}
				}
			}
		}
		
		private TreeModel CreateTreeModel ()
		{
			//it's very important that the TreeModelFilter wraps around the TreeStore or TreeModelSort
			//it looks sub-optimal since it will sort items that are filtered anyway, but the TreeModelFilter
			//messes up the RowInserted event
			TreeModel model;
			
			if (_sort)
			{
				_sortedTreeStore = new TreeModelSort (_treeStore);
				_sortedTreeStore.SetSortFunc (0, new TreeIterCompareFunc (TreeNodeSortFunc));
				_sortedTreeStore.SetSortColumnId (0, _sortOrder == SortOrder.Ascending ? SortType.Ascending : SortType.Descending);
			}
			
			if (_usingFilter)
			{
				if (_sort)
					_filteredTreeStore = new TreeModelFilter (_sortedTreeStore, null);
				else
					_filteredTreeStore = new TreeModelFilter (_treeStore, null);
					
				_filteredTreeStore.VisibleFunc = new TreeModelFilterVisibleFunc (TreeNodeFilterFunc);
				
				model = _filteredTreeStore;
			}
			else
			{
				if (_sort)
					model = _sortedTreeStore;
				else
					model = _treeStore;
			}
			
			return model;
		}
		
		public SortOrder SortOrder
		{
			get { return _sortOrder; }
			set
			{
				if (_sortOrder != value)
				{
					_sortOrder = value;
					
					if (_sort)
						_sortedTreeStore.SetSortColumnId (0, value == SortOrder.Ascending ? SortType.Ascending : SortType.Descending);
				}
			}
		}
		
		public ITreeNodeFilter Filter
		{
			get { return _filter; }
			set {
				if (_filter != value)
				{
					_filter = value;
					
					if (value == null)
					{
						_usingFilter = false;
						
						if (_sort)
							this.Model = _sortedTreeStore;
						else
							this.Model = _treeStore;
						
						if (_filteredTreeStore != null)
						{
							_filteredTreeStore.Dispose ();
							_filteredTreeStore = null;
						}
					}
					else
					{
						_usingFilter = true;
						
						if (_filteredTreeStore == null)
							this.Model = CreateTreeModel ();
						else
							this.Model = _treeStore;
						
						_filteredTreeStore.Refilter ();
					}
				}
			}
		}
		
		public void Refilter ()
		{
			if (_usingFilter)
				_filteredTreeStore.Refilter ();
		}
		
		public void Resort ()
		{
			if (_sort)
				_sortedTreeStore.ChangeSortColumn ();
		}
		
		protected virtual bool TreeNodeFilterFunc (TreeModel model, TreeIter iter)
		{
			if (model is TreeModelSort) //only convert the iter if it is coming from the TreeModelSort
				iter = _sortedTreeStore.ConvertIterToChildIter (iter);
			
			TreeNodeContext ctx = GetTreeNodeContext (iter, false);
			if (ctx == null)
				return true;
			
			return _filter.Filter (ctx);
		}
		
		protected virtual int TreeNodeSortFunc (TreeModel model, TreeIter iter1, TreeIter iter2)
		{
			//the iters are real iters and must not be converted
			TreeNodeContext ctx1 = GetTreeNodeContext (iter1, false);
			TreeNodeContext ctx2 = GetTreeNodeContext (iter2, false);
			
			if (ctx1 == null || ctx1.NodeObject == null)
			{
				if (ctx2 == null || ctx2.NodeObject == null)
					return 0;
				
				return _sortOrder == SortOrder.Ascending ? 1 : -1;
			}
			else if (ctx2 == null || ctx2.NodeObject == null)
			{
				return _sortOrder == SortOrder.Ascending ? -1 : 1;
			}
			
			return _sorter.Compare (ctx1, ctx2);
		}
		
		public string GetComparableContent (TreeNodeContext ctx)
		{
			ITreeNodeBuilder<T> nodeBuilder = GetNodeBuilder (ctx.NodeObject);
			return nodeBuilder.GetComparableContent (ctx);
		}
		
		public void AddNodeBuilder (Gui.ITreeNodeBuilder<T> nodeBuilder)
		{
			ThrowUtility.ThrowIfNull ("nodeBuilder", nodeBuilder);
			
			if (!_nodeBuilders.Contains (nodeBuilder))
				_nodeBuilders.Add (nodeBuilder);
		}
		
		public void RemoveNodeBuilder (Gui.ITreeNodeBuilder<T> nodeBuilder)
		{
			ThrowUtility.ThrowIfNull ("nodeBuilder", nodeBuilder);
			
			if (_nodeBuilders.Contains (nodeBuilder))
				_nodeBuilders.Remove (nodeBuilder);
		}
		
		public ITreeNodeBuilder<T> GetNodeBuilder (object obj)
		{
			ThrowUtility.ThrowIfNull ("obj", obj);
			
			return GetNodeBuilder (obj.GetType ());
		}
		
		public ITreeNodeBuilder<T> GetNodeBuilder (Type type)
		{
			ThrowUtility.ThrowIfNull ("type", type);
			
			foreach (ITreeNodeBuilder<T> nodeBuilder in _nodeBuilders)
			{
				if (type.Equals (nodeBuilder.NodeType))
					return nodeBuilder;
			}
			
			return _fallbackNodeBuilder;
		}
		
		public bool NodeExists (uint node)
		{
			return _iterLookup.ContainsKey (node);
		}
		
		public virtual uint AddNode (object node)
		{
			return AddNode (null, node);
		}
		
		public uint AddNode (uint? parent, object node)
		{
			ThrowUtility.ThrowIfNull ("node", node);
			
			TreeIter iter = TreeIter.Zero;
			
			if (!parent.HasValue)
			{
				_objectQueue.Enqueue (node);
				GetUniqueNodeIndex ();
				iter = _treeStore.AppendNode ();
			}
			else
			{
				TreeIter parentIter = TreeIter.Zero;
				
				if (_iterLookup.TryGetValue (parent.Value, out parentIter))
				{
					_objectQueue.Enqueue (node);
					GetUniqueNodeIndex ();
					iter = _treeStore.AppendNode (parentIter);
				}
				else
				{
					return AddNode (null, node);
				}
			}
			
			return _nodeIndex;
		}
		
		public uint[] AddNodes (object parent, object obj)
		{
			ThrowUtility.ThrowIfNull ("parent", parent);
			ThrowUtility.ThrowIfNull ("obj", obj);
			
			uint[] indices = GetNodes (parent);
			uint[] nodes = new uint[indices.Length];
			int i = 0;
			
			foreach (uint index in indices)
				nodes[i++] = AddNode (index, obj);
			
			return new uint[0];
		}
		
		protected virtual uint GetUniqueNodeIndex ()
		{
			if (_nodeIndex == uint.MaxValue)
			{
				_nodeIndex = 0; //FIXME: use better method without conflicts
				Log.Warn ("The maximum number of tree nodes is reached, this will be buggy!");
			}
			
			return ++_nodeIndex;
		}
		
		private uint AddRealNode (TreeIter iter, object value)
		{
			_treeStore.SetValue (iter, 0, value);
			
			uint uid = _nodeIndex;
			
			_iterLookup.Add (uid, iter);
			_nodeLookup.Add (iter, uid);
			
			List<uint> nodes = null;
			
			if (_objectLookup.TryGetValue (value, out nodes))
			{
				nodes.Add (uid);
			}
			else
			{
				nodes = new List<uint> ();
				nodes.Add (uid);
				
				_objectLookup.Add (value, nodes);
			}
			
			AddDummyNode (iter, uid, value);
			return uid;
		}
		
		private void AddDummyNode (TreeIter iter, uint node, object obj)
		{
			ITreeNodeBuilder<T> builder = GetNodeBuilder (obj);
			
			if (builder != null)
				_addDummy = builder.HasDynamicChildNodes (new TreeNodeContext (this, node, obj));
		}
		
		public uint InsertNode (int index, object node)
		{
			return InsertNode (index, null, node);
		}
		
		public uint InsertNode (int index, uint? parent, object node)
		{
			ThrowUtility.ThrowIfNull ("node", node);
			
			TreeIter iter = TreeIter.Zero;
			
			if (!parent.HasValue)
			{
				_objectQueue.Enqueue (node);
				GetUniqueNodeIndex ();
				iter = _treeStore.InsertNode (index);
			}
			else
			{
				TreeIter parentIter = TreeIter.Zero;
				
				if (_iterLookup.TryGetValue (parent.Value, out parentIter))
				{
					_objectQueue.Enqueue (node);
					GetUniqueNodeIndex ();
					iter = _treeStore.InsertNode (parentIter, index);
					
					// This is purely to force expansion of the tree until it gets fixed.
					ExpandNode (parent.Value);
				}
				else
				{
					AddNode (null, node);
				}
			}
			
			return _nodeIndex;
		}
		
		public bool UpdateNode (uint node)
		{
			return UpdateNode (node, false);
		}
		
		public bool UpdateNode (uint node, bool updateChildren)
		{
			TreeIter iter = TreeIter.Zero;
				
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreePath path = _treeStore.GetPath (iter);
				_treeStore.EmitRowChanged (path, iter);
				
				if (updateChildren)
				{
					foreach (uint child in GetChildren (node))
						UpdateNode (child, updateChildren);
					
					if (!_dummyNodes.ContainsKey (iter))
					{
						//no dummy child node, so remove all child nodes and build again
						//TODO: remove+add children ??
					}
				}
				
				Refilter ();
				return true;
			}
			return false;
		}
		
		public bool RemoveNode (uint node)
		{
			TreeIter iter = TreeIter.Zero;
			
			if (_iterLookup.TryGetValue (node, out iter))
			{
				_nodeLookup.Remove (iter);
				_iterLookup.Remove (node);
				
				_treeStore.Remove (ref iter);
				return true;
			}
			
			return false;
		}
		
		public object GetNodeObject (uint node)
		{
			TreeNodeContext context = GetTreeNodeContext (node);
			if (context != null)
				return context.NodeObject;
			
			return null;
		}
		
		public bool SwapNode (uint node1, uint node2)
		{
			TreeIter iter1 = TreeIter.Zero;
			
			if (_iterLookup.TryGetValue (node1, out iter1))
			{
				TreeIter iter2 = TreeIter.Zero;
				
				if (_iterLookup.TryGetValue (node2, out iter2))
				{
					_treeStore.Swap (iter1, iter2);
					return true;
				}
			}
			
			return false;
		}
		
		public int GetNodeIndex (uint node)
		{
			TreeIter iter = TreeIter.Zero;
			
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreePath path = _treeStore.GetPath (iter);
				int[] indices = path.Indices;
				int len = indices.Length;
				
				return indices[len - 1];
			}
			return -1;
		}
		
		public int[] GetNodeIndices (uint node)
		{
			TreeIter iter = TreeIter.Zero;
			
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreePath path = _treeStore.GetPath (iter);
				return path.Indices;
			}
			
			return new int[0];
		}
		
		public void Clear ()
		{
			_nodeIndex = 0;
			_treeStore.Clear ();
			
			_iterLookup.Clear ();
			_nodeLookup.Clear ();
			_objectLookup.Clear ();
		}
		
		public uint[] GetNodes (object obj)
		{
			return GetNodes (null, obj);
		}
		
		public uint[] GetNodes (uint? parent, object obj)
		{
			ThrowUtility.ThrowIfNull ("obj", obj);
			
			if (!parent.HasValue)
			{
				List<uint> nodes = null;
				
				if (_objectLookup.TryGetValue (obj, out nodes))
					return nodes.ToArray ();
			}
			else
			{
				List<uint> nodes = null;
				
				if (_objectLookup.TryGetValue (obj, out nodes))
				{
					List<uint> filter = new List<uint> ();
					
					foreach (uint node in nodes)
					{
						uint? nodeParent = GetParent (node);
						
						if (nodeParent.HasValue && nodeParent.Value == parent.Value)
							filter.Add (node);
					}
					
					return filter.ToArray ();
				}
			}
			
			return new uint[0];
		}
		
		public bool GetFirstNode (object obj, out uint node)
		{
			return GetFirstNode (null, obj, out node);
		}
		
		public bool GetFirstNode (uint? parent, object obj, out uint node)
		{
			ThrowUtility.ThrowIfNull ("obj", obj);
			
			if (!parent.HasValue)
			{
				List<uint> nodes = null;
				
				if (_objectLookup.TryGetValue (obj, out nodes))
				{
					if (nodes.Count > 0) 
					{
						node = nodes[0];
						return true;
					}
				}
			}
			else
			{
				List<uint> nodes = null;
				
				if (_objectLookup.TryGetValue (obj, out nodes))
				{
					foreach (uint n in nodes)
					{
						uint? nodeParent = GetParent (n);
						
						if (nodeParent.HasValue && nodeParent.Value == parent.Value)
						{
							node = n;
							return true;
						}
					}
				}
			}
			
			node = uint.MaxValue;
			
			return false;
		}
		
		public uint? GetParent (uint node)
		{
			TreeIter iter;
			
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreeIter parent;
				uint parentNode;
				
				if (_treeStore.IterParent (out parent, iter))
				{
					if (_nodeLookup.TryGetValue (parent, out parentNode))
						return parentNode;
				}
			}
			return null;
		}
		
		public object GetParentObject (uint node)
		{
			uint? parent = GetParent (node);
			if (parent.HasValue)
				return GetNodeObject (parent.Value);
			return null;
		}
		
		public bool GetNextNode (uint node, out uint next)
		{
			TreeIter iter = TreeIter.Zero;
			
			if (_iterLookup.TryGetValue (node, out iter))
			{
				if (_treeStore.IterNext (ref iter))
				{
					next = _nodeLookup[iter];
					return true;
				}
			}
			
			next = uint.MaxValue;
			
			return false;
		}
		
		public bool GetPreviousNode (uint node, out uint previous)
		{
			TreeIter iter = TreeIter.Zero;
			
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreePath path = _treeStore.GetPath (iter);
				
				if (path.Prev ())
				{
					_treeStore.GetIter (out iter, path);
					previous = _nodeLookup[iter];
					
					return true;
				}
			}
			
			previous = uint.MaxValue;
			return false;
		}
		
		public uint[] GetSelectedNodes ()
		{
			uint[] nodes = new uint[GetSelectionCount ()];
			int index=0;
			
			foreach (TreePath path in this.Selection.GetSelectedRows ())
			{
				uint node;
				
				if (GetNodeFromPath (path, out node))
					nodes[index++] = node;
				else
					throw new ArgumentException ("Internal error: invalid TreePath.");
			}
			
			return nodes;
		}
		
		public bool GetNodeFromPath (TreePath path, out uint node)
		{
			TreeIter iter;
			if (Model.GetIter (out iter, path))
			{
				iter = GetRealTreeIter (iter);
				if (_nodeLookup.TryGetValue (iter, out node))
					return true;
			}
			
			node = uint.MaxValue;
			return false;
		}
		
		public bool GetNodeFromIter (TreeIter iter, out uint node)
		{
			TreeIter realIter = GetRealTreeIter (iter);
			if (_nodeLookup.TryGetValue (iter, out node))
				return true;
			
			node = uint.MaxValue;
			return false;
		}
		
		public bool GetSelectedNode (out uint node)
		{
			TreeSelection selection = this.Selection;
			
			if (selection.Mode == SelectionMode.Single || selection.Mode == SelectionMode.Browse)
			{
				TreeIter iter;
				
				if (selection.GetSelected (out iter))
				{
					iter = GetRealTreeIter (iter);
					node = _nodeLookup[iter];
					return true;
				}
			}
			else if (selection.Mode == SelectionMode.Multiple || selection.Mode == SelectionMode.Extended)
			{
				if (GetSelectionCount () >= 1)
				{
					TreePath path = selection.GetSelectedRows ()[0];
					
					if (GetNodeFromPath (path, out node))
						return true;
				}
			}
			
			node = uint.MaxValue;
			return false;
		}
		
		public int GetSelectionCount ()
		{
			return this.Selection.CountSelectedRows ();
		}
		
		public bool IsNodeSelected (uint node)
		{
			TreeIter iter;
			
			if (_iterLookup.TryGetValue (node, out iter))
			{
				iter = GetVirtualTreeIter (iter);
				return this.Selection.IterIsSelected (iter);
			}
			
			return false;
		}
		
		public uint[] GetChildren (uint? parent)
		{
			TreeIter iter = TreeIter.Zero;
			
			if (!parent.HasValue)
			{
				int count = _treeStore.IterNChildren ();
				uint[] children = new uint[count];
				
				TreeIter child = TreeIter.Zero;
				int index = 0;
				
				if (_treeStore.IterNthChild (out child, 0))
				{
					do
					{
						children[index++] = _nodeLookup[child];
					}
					while (_treeStore.IterNext (ref child));
					
					return children;
				}
			}
			else
			{
				if (_iterLookup.TryGetValue (parent.Value, out iter))
				{
					int count = _treeStore.IterNChildren (iter);
					uint[] children = new uint[count];
					
					TreeIter child = TreeIter.Zero;
					int index = 0;
					
					if (_treeStore.IterNthChild (out child, iter, 0))
					{
						do
						{
							children[index++] = _nodeLookup[child];
						}
						while (_treeStore.IterNext (ref child));
						
						return children;
					}
				}
			}
			
			return new uint[0];
		}
		
		public uint[] GetChildren ()
		{
			return GetChildren (null);
		}
		
		public bool GetFirstChild (out uint node)
		{
			return GetFirstChild (null, out node);
		}
		
		public bool GetFirstChild (uint? parent, out uint node)
		{
			TreeIter child = TreeIter.Zero;
			
			if (!parent.HasValue)
			{
				if (_treeStore.IterNthChild (out child, 0))
				{
					node = _nodeLookup[child];
					return true;
				}
			}
			else
			{
				TreeIter iter = TreeIter.Zero;
				
				if (_iterLookup.TryGetValue (parent.Value, out iter))
				{
					if (_treeStore.IterNthChild (out child, iter, 0))
					{
						node = _nodeLookup[child];
						return true;
					}
				}
			}
			
			node = uint.MaxValue;
			
			return false;
		}
		
		public int GetChildCount ()
		{
			return GetChildCount (null);
		}
		
		public int GetChildCount (uint? parent)
		{
			if (!parent.HasValue)
			{
				return _treeStore.IterNChildren ();
			}
			else
			{
				TreeIter iter = TreeIter.Zero;
				
				if (_iterLookup.TryGetValue (parent.Value, out iter))
					return _treeStore.IterNChildren (iter);
			}
			
			return 0;
		}
		
		public uint[] GetVisibleChildren (uint? parent)
		{
			if (!parent.HasValue)
			{
				int count = Model.IterNChildren ();
				uint[] children = new uint[count];
				
				TreeIter child = TreeIter.Zero;
				int index = 0;
				
				if (Model.IterNthChild (out child, 0))
				{
					do
					{
						TreeIter realChild = GetRealTreeIter (child);
						children[index++] = _nodeLookup[realChild];
					} while (Model.IterNext (ref child));
					
					return children;
				}
			}
			else
			{
				TreeIter iter = TreeIter.Zero;
				if (_iterLookup.TryGetValue (parent.Value, out iter))
				{
					TreeIter virtualIter = GetVirtualTreeIter (iter);
					int count = Model.IterNChildren (virtualIter);
					uint[] children = new uint[count];
					
					TreeIter child = TreeIter.Zero;
					int index = 0;
					
					if (Model.IterNthChild (out child, virtualIter, 0))
					{
						do
						{
							TreeIter realChild = GetRealTreeIter (child);
							children[index++] = _nodeLookup[realChild];
						}
						while (Model.IterNext (ref child));
						
						return children;
					}
				}
			}
			
			return new uint[0];
		}
		
		public uint[] GetVisibleChildren ()
		{
			return GetChildren (null);
		}
		
		public bool GetVisibleFirstChild (out uint node)
		{
			return GetFirstChild (null, out node);
		}
		
		public bool GetVisibleFirstChild (uint? parent, out uint node)
		{
			TreeIter child = TreeIter.Zero;
			
			if (!parent.HasValue)
			{
				if (Model.IterNthChild (out child, 0))
				{
					TreeIter realChild = GetRealTreeIter (child);
					node = _nodeLookup[realChild];
					return true;
				}
			}
			else
			{
				TreeIter iter = TreeIter.Zero;
				
				if (_iterLookup.TryGetValue (parent.Value, out iter))
				{
					TreeIter virtualIter = GetVirtualTreeIter (iter);
					if (Model.IterNthChild (out child, virtualIter, 0))
					{
						TreeIter realChild = GetRealTreeIter (child);
						node = _nodeLookup[realChild];
						return true;
					}
				}
			}
			
			node = uint.MaxValue;
			
			return false;
		}
		
		public int GetVisibleChildCount ()
		{
			return GetChildCount (null);
		}
		
		public int GetVisibleChildCount (uint? parent)
		{
			if (!parent.HasValue)
			{
				return Model.IterNChildren ();
			}
			else
			{
				TreeIter iter = TreeIter.Zero;
				
				if (_iterLookup.TryGetValue (parent.Value, out iter))
				{
					TreeIter virtualIter = GetVirtualTreeIter (iter);
					return Model.IterNChildren (virtualIter);
				}
			}
			
			return 0;
		}
		
		public void ExpandNode (uint node)
		{
			TreeIter iter;
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreeIter virtualIter = GetVirtualTreeIter (iter);
				TreePath path = Model.GetPath (virtualIter);
				ExpandRow (path, true);
			}
		}
		
		public void CollapseNode (uint node)
		{
			TreeIter iter;
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreeIter virtualIter = GetVirtualTreeIter (iter);
				TreePath path = Model.GetPath (virtualIter);
				CollapseRow (path);
			}
		}
		
		public void SelectNode (uint node)
		{
			TreeIter iter;
			if (_iterLookup.TryGetValue (node, out iter))
			{
				TreeIter virtualIter = GetVirtualTreeIter (iter);
				TreePath path = Model.GetPath (virtualIter);
				
				SetCursor (path, Columns[0], false);
			}
		}
	}
}